package eitwitter.learning;

import eitwitter.normalization.Normalizer;
import eitwitter.storage.Tweet;
import eitwitter.storage.Vocabulary;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

/**
 * Classe permettant de stocker toutes les informations relatives
 * a une polarite
 * @author AH & PJ
 */
public class CategoryInformations {

	/** Nombre de tweets references dans la categorie */
	private int tweetsNumber;

	/** Methode de normalisation utilisee sur les tweets */
	private Normalizer normalizer;

	/** Mots associes a leurs informations */
	private HashMap<Integer, WordInformations> words;

	/** Nombre total d'occurences de tous les mots */
	private int wordsNumber;

	/** Reference vers le dictionnaire de mots */
	private Vocabulary vocabulary;

	/** Taille totale de la catégorie en terme de caractères */
	private int totalLenght=0;

	/** Mots associes a leurs informations */
	private HashMap<Integer, Double> averageTfIdf;

	/** Statistiques de la categorie */
	private Statistics statistics;
	
	/** Classe learning associee */
	private Learning learning;
	
	/**
	 * Constructeur des informations sur la categorie
	 * initialise uniquement les attributs
	 */
	public CategoryInformations(Normalizer normalizer, Vocabulary vocabulary, Learning learning) {
		this.tweetsNumber	= 0;
		this.wordsNumber	= 0;
		this.vocabulary		= vocabulary;
		this.words			= new HashMap<Integer, WordInformations>();
		this.normalizer		= normalizer;
		this.averageTfIdf	= new HashMap<Integer, Double>();
		this.statistics		= new Statistics();
		this.learning		= learning;
	}

	/**
	 * Reference un nouveau tweet dans la categorie. 
	 * Extrait toutes les informations du tweet.
	 * @param tweet Tweet a referencer dans la categorie
	 */
	public void addTweet(Tweet tweet){
		this.tweetsNumber++;
		this.totalLenght += tweet.lenght();

		// Decoupage du corps du tweet
		HashMap<String, Integer> tweetWords = normalizer.normalize(tweet.getContent());

		// Stockage de chacun des tokens
		for(Map.Entry<String, Integer> entry : tweetWords.entrySet()){

			// On essaye de recuperer l'identifiant du token dans le dico
			int id = vocabulary.getId(entry.getKey());

			// Si le mot n'est pas present
			if(id == -1){
				id = vocabulary.size();
				vocabulary.put(vocabulary.size(), entry.getKey());
			}

			// Si le mot n'est pas deja enregistre, on l'ajoute
			if(!this.words.containsKey(id)){
				this.words.put(id, new WordInformations());
			}

			// Recuperation des informations sur le mot
			WordInformations wordInformations= this.words.get(id);

			// On incremente la frequence du mot
			wordInformations.addFrequency(entry.getValue());

			// On ajoute le nombre d'occurences du mot au nombre total d'occurences
			this.wordsNumber += entry.getValue();

			// On incremente le nombre de documents ou se trouve le mot
			wordInformations.addDocumentsNumber(1);

		}
	}

	public HashMap<Integer, WordInformations> getWords() {
		return words;
	}

	/**
	 * Retire les mots passes en parametre s'ils ont ete enregistres
	 * @param stopwords Mots a retirer du vocabulaire de la categorie
	 */
	public void removeWords(ArrayList<Integer> words) {

		// Parcours de tous les mots a supprimer
		for(Integer word : words){

			// Recuperation des informations concernant le mot a supprimer
			WordInformations wordInformations = this.words.get(word);

			// Si le mot existe bien...
			if(wordInformations != null){

				// ... On retire son nomnbre d'occurences au nombre total
				this.wordsNumber -= wordInformations.getFrequency();

				// ... Et on supprime le mot
				this.words.remove(word);
			}
		}
	}

	/**
	 * Contruit les statistiques d'une catégorie
	 */
	public void makeStatistics(){
		this.statistics.setNumberOfWords(wordsNumber);
		this.statistics.setNumberOfDifferentWords(words.size());
		this.statistics.setAverageLenght(this.totalLenght/this.tweetsNumber);

		// Calcul des tf.idf de chaque mot pour voir s'ils sont significatifs
		makeAverageTfIdf();

		// TreeMap liant les mots à leur fréquence
		HashMap<Integer, Integer> frequentWords = new HashMap<Integer, Integer>();
		// HashMap liant les mots à leut tf.idf
		HashMap<Integer, Double> significantWords = new HashMap<Integer, Double>();

		// Pour tous les mots de la catégorie
		for(Entry<Integer, WordInformations> entry : words.entrySet()){
			// On les ajoute avec leur fréquence à une TreeMap (ordonn�e)
			frequentWords.put(entry.getKey(), entry.getValue().getFrequency());

			// On les ajoute avec leur moyenne de tf.idf à une TreeMap
			significantWords.put(entry.getKey(), this.averageTfIdf.get(entry.getKey()));

		}	
		
		// On tri sur les valeurs -> fréquence ascendantes
		ValueComparatorInt vci = new ValueComparatorInt(frequentWords);
		TreeMap<Integer, Integer> frequentWords_sorted = new TreeMap<Integer, Integer>(vci);
		frequentWords_sorted.putAll(frequentWords);
		
		// On tri sur les valeurs -> tf.idf ascendant
		ValueComparatorDouble vcdA = new ValueComparatorDouble(significantWords, true);
		TreeMap<Integer, Double> significantWords_sorted = new TreeMap<Integer, Double>(vcdA);
		significantWords_sorted.putAll(significantWords);

		// On tri sur les valeurs -> tf.idf descendant
		ValueComparatorDouble vcdD = new ValueComparatorDouble(significantWords, false);
		TreeMap<Integer, Double> unsignificantWords_sorted = new TreeMap<Integer, Double>(vcdD);
		unsignificantWords_sorted.putAll(significantWords);

		// On ne garde que les 20 premiers
		TreeMap<Integer, Integer> frequentWords_sorted2 = new TreeMap<Integer, Integer>();
		for(Entry<Integer, Integer> entry : frequentWords_sorted.entrySet()) {
			if(frequentWords_sorted2.size()<20)
				frequentWords_sorted2.put(entry.getKey(), entry.getValue());
		}

		// On ne garde que les 20 premiers
		TreeMap<Integer, Double> significantWords_sorted2 = new TreeMap<Integer, Double>();
		for(Entry<Integer, Double> entry : significantWords_sorted.entrySet()) {
			if(significantWords_sorted2.size()<20)
				significantWords_sorted2.put(entry.getKey(), entry.getValue());
		}
		
		// On ne garde que les 20 premiers
		TreeMap<Integer, Double> unsignificantWords_sorted2 = new TreeMap<Integer, Double>();
		for(Entry<Integer, Double> entry : unsignificantWords_sorted.entrySet()) {
			if(unsignificantWords_sorted2.size()<20)
				unsignificantWords_sorted2.put(entry.getKey(), entry.getValue());
		}
		
		this.statistics.setFrequentWords(frequentWords_sorted2);
		this.statistics.setSignificantWords(significantWords_sorted2);
		this.statistics.setUnsignificantWords(unsignificantWords_sorted2);

	}
	
	/**
	 * Calcul la moyenne des tf.idf pour chaque mot de la catégorie
	 */
	public void makeAverageTfIdf(){
		for(Entry<Integer, WordInformations> entry : words.entrySet()){
			// log (NbTotalTweets / NbDeTweetsContenant(w))
			double idf = Math.log(this.tweetsNumber/entry.getValue().getDocumentsNumber());
			double tfidf = idf* entry.getValue().getFrequency()/this.tweetsNumber;
			this.averageTfIdf.put(entry.getKey(), tfidf);
		}

	}

	public HashMap<Integer, Double> getAverageTfIdf() {
		return averageTfIdf;
	}

	public void setAverageTfIdf(HashMap<Integer, Double> averageTfIdf) {
		this.averageTfIdf = averageTfIdf;
	}

	public Statistics getStatistics() {
		return statistics;
	}

	public void setStatistics(Statistics statistics) {
		this.statistics = statistics;
	}

	public int getWordsNumber() {
		return wordsNumber;
	}

	public int getDisctinctWordsNumber(){
		return this.words.size();
	}

	public int getTweetsNumber() {
		return tweetsNumber;
	}

	public Normalizer getNormalizer() {
		return normalizer;
	}

	public Vocabulary getVocabulary() {
		return vocabulary;
	}

	public Learning getLearning() {
		return learning;
	}
	
	


}
